CS 484 - Introduction to Machine Learning¶

Project: Churned Prediction System for Telecommunications Industry¶

Team Members:¶

Denish Dalsukhbhai Asodariya (A20525465)¶

Prince Jayantbhai Rajodiya (A20536409)¶

Enviroment Setup¶

In [4]:
!pip install tensorflow

from tensorflow.python.client import device_lib

def check_gpu():
  """Checks if a GPU is available."""
  devices = device_lib.list_local_devices()
  for device in devices:
    if 'GPU' in device.name:
      return True
  return False

if check_gpu():
  print('Found GPU!')
else:
  print('No GPU found.')
Requirement already satisfied: tensorflow in c:\users\asoda\appdata\roaming\python\python311\site-packages (2.18.0)
Requirement already satisfied: tensorflow-intel==2.18.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow) (2.18.0)
Requirement already satisfied: absl-py>=1.0.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (2.1.0)
Requirement already satisfied: astunparse>=1.6.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (1.6.3)
Requirement already satisfied: flatbuffers>=24.3.25 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (24.3.25)
Requirement already satisfied: gast!=0.5.0,!=0.5.1,!=0.5.2,>=0.2.1 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (0.6.0)
Requirement already satisfied: google-pasta>=0.1.1 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (0.2.0)
Requirement already satisfied: libclang>=13.0.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (18.1.1)
Requirement already satisfied: opt-einsum>=2.3.2 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (3.4.0)
Requirement already satisfied: packaging in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorflow-intel==2.18.0->tensorflow) (24.1)
Requirement already satisfied: protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0dev,>=3.20.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorflow-intel==2.18.0->tensorflow) (5.29.0)
Requirement already satisfied: requests<3,>=2.21.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorflow-intel==2.18.0->tensorflow) (2.32.3)
Requirement already satisfied: setuptools in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorflow-intel==2.18.0->tensorflow) (75.1.0)
Requirement already satisfied: six>=1.12.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorflow-intel==2.18.0->tensorflow) (1.16.0)
Requirement already satisfied: termcolor>=1.1.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (2.5.0)
Requirement already satisfied: typing-extensions>=3.6.6 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorflow-intel==2.18.0->tensorflow) (4.11.0)
Requirement already satisfied: wrapt>=1.11.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorflow-intel==2.18.0->tensorflow) (1.16.0)
Requirement already satisfied: grpcio<2.0,>=1.24.3 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (1.67.1)
Requirement already satisfied: tensorboard<2.19,>=2.18 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (2.18.0)
Requirement already satisfied: keras>=3.5.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (3.6.0)
Requirement already satisfied: numpy<2.1.0,>=1.26.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorflow-intel==2.18.0->tensorflow) (1.26.4)
Requirement already satisfied: h5py>=3.11.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (3.12.1)
Requirement already satisfied: ml-dtypes<0.5.0,>=0.4.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (0.4.1)
Requirement already satisfied: tensorflow-io-gcs-filesystem>=0.23.1 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorflow-intel==2.18.0->tensorflow) (0.31.0)
Requirement already satisfied: wheel<1.0,>=0.23.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from astunparse>=1.6.0->tensorflow-intel==2.18.0->tensorflow) (0.44.0)
Requirement already satisfied: rich in c:\users\asoda\appdata\roaming\python\python311\site-packages (from keras>=3.5.0->tensorflow-intel==2.18.0->tensorflow) (13.9.4)
Requirement already satisfied: namex in c:\users\asoda\appdata\roaming\python\python311\site-packages (from keras>=3.5.0->tensorflow-intel==2.18.0->tensorflow) (0.0.8)
Requirement already satisfied: optree in c:\users\asoda\appdata\roaming\python\python311\site-packages (from keras>=3.5.0->tensorflow-intel==2.18.0->tensorflow) (0.13.1)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests<3,>=2.21.0->tensorflow-intel==2.18.0->tensorflow) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests<3,>=2.21.0->tensorflow-intel==2.18.0->tensorflow) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests<3,>=2.21.0->tensorflow-intel==2.18.0->tensorflow) (2.2.3)
Requirement already satisfied: certifi>=2017.4.17 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests<3,>=2.21.0->tensorflow-intel==2.18.0->tensorflow) (2024.8.30)
Requirement already satisfied: markdown>=2.6.8 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorboard<2.19,>=2.18->tensorflow-intel==2.18.0->tensorflow) (3.7)
Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from tensorboard<2.19,>=2.18->tensorflow-intel==2.18.0->tensorflow) (0.7.2)
Requirement already satisfied: werkzeug>=1.0.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tensorboard<2.19,>=2.18->tensorflow-intel==2.18.0->tensorflow) (3.1.3)
Requirement already satisfied: MarkupSafe>=2.1.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from werkzeug>=1.0.1->tensorboard<2.19,>=2.18->tensorflow-intel==2.18.0->tensorflow) (2.1.3)
Requirement already satisfied: markdown-it-py>=2.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from rich->keras>=3.5.0->tensorflow-intel==2.18.0->tensorflow) (2.2.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from rich->keras>=3.5.0->tensorflow-intel==2.18.0->tensorflow) (2.15.1)
Requirement already satisfied: mdurl~=0.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from markdown-it-py>=2.2.0->rich->keras>=3.5.0->tensorflow-intel==2.18.0->tensorflow) (0.1.0)
No GPU found.
In [5]:
runtime_type = 'GPU'

Required Libraries¶

In [7]:
!pip install ipywidgets
!pip install keplergl
!pip install h3
!pip install h3pandas
!pip install branca
!pip install imbalanced-learn
!pip install seaborn
!pip install plotly
!pip install scikit-learn
!pip install xgboost
!pip install folium
!pip install gdown
!jupyter nbextension install --py --sys-prefix keplergl
!jupyter nbextension enable keplergl --py --sys-prefix
Requirement already satisfied: ipywidgets in c:\users\asoda\appdata\roaming\python\python311\site-packages (7.8.5)
Requirement already satisfied: comm>=0.1.3 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from ipywidgets) (0.2.2)
Requirement already satisfied: ipython-genutils~=0.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipywidgets) (0.2.0)
Requirement already satisfied: traitlets>=4.3.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipywidgets) (5.14.3)
Requirement already satisfied: widgetsnbextension~=3.6.10 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from ipywidgets) (3.6.10)
Requirement already satisfied: ipython>=4.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipywidgets) (8.27.0)
Requirement already satisfied: jupyterlab-widgets<3,>=1.0.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from ipywidgets) (1.1.11)
Requirement already satisfied: decorator in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets) (5.1.1)
Requirement already satisfied: jedi>=0.16 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets) (0.19.1)
Requirement already satisfied: matplotlib-inline in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets) (0.1.6)
Requirement already satisfied: prompt-toolkit<3.1.0,>=3.0.41 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets) (3.0.43)
Requirement already satisfied: pygments>=2.4.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets) (2.15.1)
Requirement already satisfied: stack-data in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets) (0.2.0)
Requirement already satisfied: typing-extensions>=4.6 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets) (4.11.0)
Requirement already satisfied: colorama in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets) (0.4.6)
Requirement already satisfied: notebook>=4.4.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from widgetsnbextension~=3.6.10->ipywidgets) (7.2.2)
Requirement already satisfied: parso<0.9.0,>=0.8.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jedi>=0.16->ipython>=4.0.0->ipywidgets) (0.8.3)
Requirement already satisfied: jupyter-server<3,>=2.4.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.14.1)
Requirement already satisfied: jupyterlab-server<3,>=2.27.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.27.3)
Requirement already satisfied: jupyterlab<4.3,>=4.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (4.2.5)
Requirement already satisfied: notebook-shim<0.3,>=0.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.2.3)
Requirement already satisfied: tornado>=6.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (6.4.1)
Requirement already satisfied: wcwidth in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from prompt-toolkit<3.1.0,>=3.0.41->ipython>=4.0.0->ipywidgets) (0.2.5)
Requirement already satisfied: executing in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from stack-data->ipython>=4.0.0->ipywidgets) (0.8.3)
Requirement already satisfied: asttokens in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from stack-data->ipython>=4.0.0->ipywidgets) (2.0.5)
Requirement already satisfied: pure-eval in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from stack-data->ipython>=4.0.0->ipywidgets) (0.2.2)
Requirement already satisfied: anyio>=3.1.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (4.6.2)
Requirement already satisfied: argon2-cffi>=21.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (21.3.0)
Requirement already satisfied: jinja2>=3.0.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (3.1.4)
Requirement already satisfied: jupyter-client>=7.4.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (8.6.0)
Requirement already satisfied: jupyter-core!=5.0.*,>=4.12 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (5.7.2)
Requirement already satisfied: jupyter-events>=0.9.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.10.0)
Requirement already satisfied: jupyter-server-terminals>=0.4.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.4.4)
Requirement already satisfied: nbconvert>=6.4.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (7.16.4)
Requirement already satisfied: nbformat>=5.3.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (5.10.4)
Requirement already satisfied: overrides>=5.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (7.4.0)
Requirement already satisfied: packaging>=22.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (24.1)
Requirement already satisfied: prometheus-client>=0.9 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.14.1)
Requirement already satisfied: pywinpty>=2.0.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.0.10)
Requirement already satisfied: pyzmq>=24 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (25.1.2)
Requirement already satisfied: send2trash>=1.8.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.8.2)
Requirement already satisfied: terminado>=0.8.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.17.1)
Requirement already satisfied: websocket-client>=1.7 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.8.0)
Requirement already satisfied: async-lru>=1.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.0.4)
Requirement already satisfied: httpx>=0.25.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.27.0)
Requirement already satisfied: ipykernel>=6.5.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (6.29.5)
Requirement already satisfied: jupyter-lsp>=2.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.2.5)
Requirement already satisfied: setuptools>=40.1.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (75.1.0)
Requirement already satisfied: babel>=2.10 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.11.0)
Requirement already satisfied: json5>=0.9.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.9.25)
Requirement already satisfied: jsonschema>=4.18.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (4.23.0)
Requirement already satisfied: requests>=2.31 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.32.3)
Requirement already satisfied: six in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from asttokens->stack-data->ipython>=4.0.0->ipywidgets) (1.16.0)
Requirement already satisfied: idna>=2.8 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from anyio>=3.1.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (3.7)
Requirement already satisfied: sniffio>=1.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from anyio>=3.1.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.3.0)
Requirement already satisfied: argon2-cffi-bindings in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from argon2-cffi>=21.1->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (21.2.0)
Requirement already satisfied: pytz>=2015.7 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from babel>=2.10->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2024.1)
Requirement already satisfied: certifi in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from httpx>=0.25.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2024.8.30)
Requirement already satisfied: httpcore==1.* in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from httpx>=0.25.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.0.2)
Requirement already satisfied: h11<0.15,>=0.13 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from httpcore==1.*->httpx>=0.25.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.14.0)
Requirement already satisfied: debugpy>=1.6.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipykernel>=6.5.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.6.7)
Requirement already satisfied: nest-asyncio in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipykernel>=6.5.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.6.0)
Requirement already satisfied: psutil in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipykernel>=6.5.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (5.9.0)
Requirement already satisfied: MarkupSafe>=2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jinja2>=3.0.3->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.1.3)
Requirement already satisfied: attrs>=22.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema>=4.18.0->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (24.2.0)
Requirement already satisfied: jsonschema-specifications>=2023.03.6 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema>=4.18.0->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2023.7.1)
Requirement already satisfied: referencing>=0.28.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema>=4.18.0->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.30.2)
Requirement already satisfied: rpds-py>=0.7.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema>=4.18.0->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.10.6)
Requirement already satisfied: python-dateutil>=2.8.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-client>=7.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.9.0.post0)
Requirement already satisfied: platformdirs>=2.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-core!=5.0.*,>=4.12->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (3.10.0)
Requirement already satisfied: pywin32>=300 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-core!=5.0.*,>=4.12->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (305.1)
Requirement already satisfied: python-json-logger>=2.0.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.0.7)
Requirement already satisfied: pyyaml>=5.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (6.0.2)
Requirement already satisfied: rfc3339-validator in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.1.4)
Requirement already satisfied: rfc3986-validator>=0.1.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.1.1)
Requirement already satisfied: beautifulsoup4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (4.12.3)
Requirement already satisfied: bleach!=5.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (4.1.0)
Requirement already satisfied: defusedxml in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.7.1)
Requirement already satisfied: jupyterlab-pygments in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.1.2)
Requirement already satisfied: mistune<4,>=2.0.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.0.4)
Requirement already satisfied: nbclient>=0.5.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.8.0)
Requirement already satisfied: pandocfilters>=1.4.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.5.0)
Requirement already satisfied: tinycss2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.2.1)
Requirement already satisfied: fastjsonschema>=2.15 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbformat>=5.3.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.16.2)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests>=2.31->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (3.3.2)
Requirement already satisfied: urllib3<3,>=1.21.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests>=2.31->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.2.3)
Requirement already satisfied: webencodings in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from bleach!=5.0.0->nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (0.5.1)
Requirement already satisfied: fqdn in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.5.1)
Requirement already satisfied: isoduration in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (20.11.0)
Requirement already satisfied: jsonpointer>1.13 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (3.0.0)
Requirement already satisfied: uri-template in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.3.0)
Requirement already satisfied: webcolors>=24.6.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (24.11.1)
Requirement already satisfied: cffi>=1.0.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from argon2-cffi-bindings->argon2-cffi>=21.1->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.17.1)
Requirement already satisfied: soupsieve>1.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from beautifulsoup4->nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.5)
Requirement already satisfied: pycparser in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from cffi>=1.0.1->argon2-cffi-bindings->argon2-cffi>=21.1->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.21)
Requirement already satisfied: arrow>=0.15.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from isoduration->jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (1.3.0)
Requirement already satisfied: types-python-dateutil>=2.8.10 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from arrow>=0.15.0->isoduration->jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets) (2.9.0.20241003)
Requirement already satisfied: keplergl in c:\programdata\anaconda3\envs\myenv\lib\site-packages (0.3.2)
Requirement already satisfied: ipywidgets<8,>=7.0.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from keplergl) (7.8.5)
Requirement already satisfied: traittypes>=0.2.1 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from keplergl) (0.2.1)
Requirement already satisfied: geopandas>=0.5.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from keplergl) (1.0.1)
Requirement already satisfied: pandas>=0.23.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from keplergl) (2.2.3)
Requirement already satisfied: Shapely>=1.6.4.post2 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from keplergl) (2.0.6)
Requirement already satisfied: numpy>=1.22 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from geopandas>=0.5.0->keplergl) (1.26.4)
Requirement already satisfied: pyogrio>=0.7.2 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from geopandas>=0.5.0->keplergl) (0.10.0)
Requirement already satisfied: packaging in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from geopandas>=0.5.0->keplergl) (24.1)
Requirement already satisfied: pyproj>=3.3.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from geopandas>=0.5.0->keplergl) (3.7.0)
Requirement already satisfied: comm>=0.1.3 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from ipywidgets<8,>=7.0.0->keplergl) (0.2.2)
Requirement already satisfied: ipython-genutils~=0.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipywidgets<8,>=7.0.0->keplergl) (0.2.0)
Requirement already satisfied: traitlets>=4.3.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipywidgets<8,>=7.0.0->keplergl) (5.14.3)
Requirement already satisfied: widgetsnbextension~=3.6.10 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from ipywidgets<8,>=7.0.0->keplergl) (3.6.10)
Requirement already satisfied: ipython>=4.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipywidgets<8,>=7.0.0->keplergl) (8.27.0)
Requirement already satisfied: jupyterlab-widgets<3,>=1.0.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from ipywidgets<8,>=7.0.0->keplergl) (1.1.11)
Requirement already satisfied: python-dateutil>=2.8.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pandas>=0.23.0->keplergl) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pandas>=0.23.0->keplergl) (2024.1)
Requirement already satisfied: tzdata>=2022.7 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pandas>=0.23.0->keplergl) (2024.2)
Requirement already satisfied: decorator in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (5.1.1)
Requirement already satisfied: jedi>=0.16 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (0.19.1)
Requirement already satisfied: matplotlib-inline in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (0.1.6)
Requirement already satisfied: prompt-toolkit<3.1.0,>=3.0.41 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (3.0.43)
Requirement already satisfied: pygments>=2.4.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (2.15.1)
Requirement already satisfied: stack-data in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (0.2.0)
Requirement already satisfied: typing-extensions>=4.6 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (4.11.0)
Requirement already satisfied: colorama in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (0.4.6)
Requirement already satisfied: certifi in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pyogrio>=0.7.2->geopandas>=0.5.0->keplergl) (2024.8.30)
Requirement already satisfied: six>=1.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from python-dateutil>=2.8.2->pandas>=0.23.0->keplergl) (1.16.0)
Requirement already satisfied: notebook>=4.4.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (7.2.2)
Requirement already satisfied: parso<0.9.0,>=0.8.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jedi>=0.16->ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (0.8.3)
Requirement already satisfied: jupyter-server<3,>=2.4.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.14.1)
Requirement already satisfied: jupyterlab-server<3,>=2.27.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.27.3)
Requirement already satisfied: jupyterlab<4.3,>=4.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (4.2.5)
Requirement already satisfied: notebook-shim<0.3,>=0.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.2.3)
Requirement already satisfied: tornado>=6.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (6.4.1)
Requirement already satisfied: wcwidth in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from prompt-toolkit<3.1.0,>=3.0.41->ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (0.2.5)
Requirement already satisfied: executing in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from stack-data->ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (0.8.3)
Requirement already satisfied: asttokens in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from stack-data->ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (2.0.5)
Requirement already satisfied: pure-eval in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from stack-data->ipython>=4.0.0->ipywidgets<8,>=7.0.0->keplergl) (0.2.2)
Requirement already satisfied: anyio>=3.1.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (4.6.2)
Requirement already satisfied: argon2-cffi>=21.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (21.3.0)
Requirement already satisfied: jinja2>=3.0.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (3.1.4)
Requirement already satisfied: jupyter-client>=7.4.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (8.6.0)
Requirement already satisfied: jupyter-core!=5.0.*,>=4.12 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (5.7.2)
Requirement already satisfied: jupyter-events>=0.9.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.10.0)
Requirement already satisfied: jupyter-server-terminals>=0.4.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.4.4)
Requirement already satisfied: nbconvert>=6.4.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (7.16.4)
Requirement already satisfied: nbformat>=5.3.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (5.10.4)
Requirement already satisfied: overrides>=5.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (7.4.0)
Requirement already satisfied: prometheus-client>=0.9 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.14.1)
Requirement already satisfied: pywinpty>=2.0.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.0.10)
Requirement already satisfied: pyzmq>=24 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (25.1.2)
Requirement already satisfied: send2trash>=1.8.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.8.2)
Requirement already satisfied: terminado>=0.8.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.17.1)
Requirement already satisfied: websocket-client>=1.7 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.8.0)
Requirement already satisfied: async-lru>=1.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.0.4)
Requirement already satisfied: httpx>=0.25.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.27.0)
Requirement already satisfied: ipykernel>=6.5.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (6.29.5)
Requirement already satisfied: jupyter-lsp>=2.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.2.5)
Requirement already satisfied: setuptools>=40.1.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (75.1.0)
Requirement already satisfied: babel>=2.10 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.11.0)
Requirement already satisfied: json5>=0.9.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.9.25)
Requirement already satisfied: jsonschema>=4.18.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (4.23.0)
Requirement already satisfied: requests>=2.31 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.32.3)
Requirement already satisfied: idna>=2.8 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from anyio>=3.1.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (3.7)
Requirement already satisfied: sniffio>=1.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from anyio>=3.1.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.3.0)
Requirement already satisfied: argon2-cffi-bindings in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from argon2-cffi>=21.1->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (21.2.0)
Requirement already satisfied: httpcore==1.* in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from httpx>=0.25.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.0.2)
Requirement already satisfied: h11<0.15,>=0.13 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from httpcore==1.*->httpx>=0.25.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.14.0)
Requirement already satisfied: debugpy>=1.6.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipykernel>=6.5.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.6.7)
Requirement already satisfied: nest-asyncio in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipykernel>=6.5.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.6.0)
Requirement already satisfied: psutil in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from ipykernel>=6.5.0->jupyterlab<4.3,>=4.2.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (5.9.0)
Requirement already satisfied: MarkupSafe>=2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jinja2>=3.0.3->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.1.3)
Requirement already satisfied: attrs>=22.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema>=4.18.0->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (24.2.0)
Requirement already satisfied: jsonschema-specifications>=2023.03.6 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema>=4.18.0->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2023.7.1)
Requirement already satisfied: referencing>=0.28.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema>=4.18.0->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.30.2)
Requirement already satisfied: rpds-py>=0.7.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema>=4.18.0->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.10.6)
Requirement already satisfied: platformdirs>=2.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-core!=5.0.*,>=4.12->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (3.10.0)
Requirement already satisfied: pywin32>=300 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-core!=5.0.*,>=4.12->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (305.1)
Requirement already satisfied: python-json-logger>=2.0.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.0.7)
Requirement already satisfied: pyyaml>=5.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (6.0.2)
Requirement already satisfied: rfc3339-validator in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.1.4)
Requirement already satisfied: rfc3986-validator>=0.1.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.1.1)
Requirement already satisfied: beautifulsoup4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (4.12.3)
Requirement already satisfied: bleach!=5.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (4.1.0)
Requirement already satisfied: defusedxml in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.7.1)
Requirement already satisfied: jupyterlab-pygments in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.1.2)
Requirement already satisfied: mistune<4,>=2.0.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.0.4)
Requirement already satisfied: nbclient>=0.5.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.8.0)
Requirement already satisfied: pandocfilters>=1.4.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.5.0)
Requirement already satisfied: tinycss2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.2.1)
Requirement already satisfied: fastjsonschema>=2.15 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from nbformat>=5.3.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.16.2)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests>=2.31->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (3.3.2)
Requirement already satisfied: urllib3<3,>=1.21.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests>=2.31->jupyterlab-server<3,>=2.27.1->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.2.3)
Requirement already satisfied: webencodings in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from bleach!=5.0.0->nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (0.5.1)
Requirement already satisfied: fqdn in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.5.1)
Requirement already satisfied: isoduration in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (20.11.0)
Requirement already satisfied: jsonpointer>1.13 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (3.0.0)
Requirement already satisfied: uri-template in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.3.0)
Requirement already satisfied: webcolors>=24.6.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (24.11.1)
Requirement already satisfied: cffi>=1.0.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from argon2-cffi-bindings->argon2-cffi>=21.1->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.17.1)
Requirement already satisfied: soupsieve>1.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from beautifulsoup4->nbconvert>=6.4.4->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.5)
Requirement already satisfied: pycparser in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from cffi>=1.0.1->argon2-cffi-bindings->argon2-cffi>=21.1->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.21)
Requirement already satisfied: arrow>=0.15.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from isoduration->jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (1.3.0)
Requirement already satisfied: types-python-dateutil>=2.8.10 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from arrow>=0.15.0->isoduration->jsonschema[format-nongpl]>=4.18.0->jupyter-events>=0.9.0->jupyter-server<3,>=2.4.0->notebook>=4.4.1->widgetsnbextension~=3.6.10->ipywidgets<8,>=7.0.0->keplergl) (2.9.0.20241003)
Requirement already satisfied: h3 in c:\users\asoda\appdata\roaming\python\python311\site-packages (4.1.2)
Requirement already satisfied: h3pandas in c:\users\asoda\appdata\roaming\python\python311\site-packages (0.2.6)
Requirement already satisfied: geopandas in c:\users\asoda\appdata\roaming\python\python311\site-packages (from h3pandas) (1.0.1)
Requirement already satisfied: numpy in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from h3pandas) (1.26.4)
Requirement already satisfied: pandas in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from h3pandas) (2.2.3)
Requirement already satisfied: shapely in c:\users\asoda\appdata\roaming\python\python311\site-packages (from h3pandas) (2.0.6)
Requirement already satisfied: h3 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from h3pandas) (4.1.2)
Requirement already satisfied: typing-extensions in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from h3pandas) (4.11.0)
Requirement already satisfied: pyogrio>=0.7.2 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from geopandas->h3pandas) (0.10.0)
Requirement already satisfied: packaging in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from geopandas->h3pandas) (24.1)
Requirement already satisfied: pyproj>=3.3.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from geopandas->h3pandas) (3.7.0)
Requirement already satisfied: python-dateutil>=2.8.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pandas->h3pandas) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pandas->h3pandas) (2024.1)
Requirement already satisfied: tzdata>=2022.7 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pandas->h3pandas) (2024.2)
Requirement already satisfied: certifi in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pyogrio>=0.7.2->geopandas->h3pandas) (2024.8.30)
Requirement already satisfied: six>=1.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from python-dateutil>=2.8.2->pandas->h3pandas) (1.16.0)
Requirement already satisfied: branca in c:\users\asoda\appdata\roaming\python\python311\site-packages (0.8.0)
Requirement already satisfied: jinja2>=3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from branca) (3.1.4)
Requirement already satisfied: MarkupSafe>=2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jinja2>=3->branca) (2.1.3)
Requirement already satisfied: imbalanced-learn in c:\programdata\anaconda3\envs\myenv\lib\site-packages (0.12.4)
Requirement already satisfied: numpy>=1.17.3 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from imbalanced-learn) (1.26.4)
Requirement already satisfied: scipy>=1.5.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from imbalanced-learn) (1.14.1)
Requirement already satisfied: scikit-learn>=1.0.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from imbalanced-learn) (1.5.2)
Requirement already satisfied: joblib>=1.1.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from imbalanced-learn) (1.4.2)
Requirement already satisfied: threadpoolctl>=2.0.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from imbalanced-learn) (3.5.0)
Requirement already satisfied: seaborn in c:\programdata\anaconda3\envs\myenv\lib\site-packages (0.13.2)
Requirement already satisfied: numpy!=1.24.0,>=1.20 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from seaborn) (1.26.4)
Requirement already satisfied: pandas>=1.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from seaborn) (2.2.3)
Requirement already satisfied: matplotlib!=3.6.1,>=3.4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from seaborn) (3.9.2)
Requirement already satisfied: contourpy>=1.0.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.3.1)
Requirement already satisfied: cycler>=0.10 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (4.55.0)
Requirement already satisfied: kiwisolver>=1.3.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.4.7)
Requirement already satisfied: packaging>=20.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (24.1)
Requirement already satisfied: pillow>=8 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (11.0.0)
Requirement already satisfied: pyparsing>=2.3.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (3.2.0)
Requirement already satisfied: python-dateutil>=2.7 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pandas>=1.2->seaborn) (2024.1)
Requirement already satisfied: tzdata>=2022.7 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from pandas>=1.2->seaborn) (2024.2)
Requirement already satisfied: six>=1.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn) (1.16.0)
Requirement already satisfied: plotly in c:\programdata\anaconda3\envs\myenv\lib\site-packages (5.24.1)
Requirement already satisfied: tenacity>=6.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from plotly) (9.0.0)
Requirement already satisfied: packaging in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from plotly) (24.1)
Requirement already satisfied: scikit-learn in c:\programdata\anaconda3\envs\myenv\lib\site-packages (1.5.2)
Requirement already satisfied: numpy>=1.19.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from scikit-learn) (1.26.4)
Requirement already satisfied: scipy>=1.6.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from scikit-learn) (1.14.1)
Requirement already satisfied: joblib>=1.2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from scikit-learn) (1.4.2)
Requirement already satisfied: threadpoolctl>=3.1.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from scikit-learn) (3.5.0)
Requirement already satisfied: xgboost in c:\programdata\anaconda3\envs\myenv\lib\site-packages (2.1.2)
Requirement already satisfied: numpy in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from xgboost) (1.26.4)
Requirement already satisfied: scipy in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from xgboost) (1.14.1)
Requirement already satisfied: folium in c:\programdata\anaconda3\envs\myenv\lib\site-packages (0.18.0)
Requirement already satisfied: branca>=0.6.0 in c:\users\asoda\appdata\roaming\python\python311\site-packages (from folium) (0.8.0)
Requirement already satisfied: jinja2>=2.9 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from folium) (3.1.4)
Requirement already satisfied: numpy in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from folium) (1.26.4)
Requirement already satisfied: requests in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from folium) (2.32.3)
Requirement already satisfied: xyzservices in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from folium) (2024.9.0)
Requirement already satisfied: MarkupSafe>=2.0 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from jinja2>=2.9->folium) (2.1.3)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests->folium) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests->folium) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests->folium) (2.2.3)
Requirement already satisfied: certifi>=2017.4.17 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests->folium) (2024.8.30)
Requirement already satisfied: gdown in c:\programdata\anaconda3\envs\myenv\lib\site-packages (5.2.0)
Requirement already satisfied: beautifulsoup4 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from gdown) (4.12.3)
Requirement already satisfied: filelock in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from gdown) (3.16.1)
Requirement already satisfied: requests[socks] in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from gdown) (2.32.3)
Requirement already satisfied: tqdm in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from gdown) (4.67.0)
Requirement already satisfied: soupsieve>1.2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from beautifulsoup4->gdown) (2.5)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests[socks]->gdown) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests[socks]->gdown) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests[socks]->gdown) (2.2.3)
Requirement already satisfied: certifi>=2017.4.17 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests[socks]->gdown) (2024.8.30)
Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from requests[socks]->gdown) (1.7.1)
Requirement already satisfied: colorama in c:\programdata\anaconda3\envs\myenv\lib\site-packages (from tqdm->gdown) (0.4.6)
Installing C:\ProgramData\anaconda3\Lib\site-packages\keplergl\static -> keplergl-jupyter
Up to date: C:\ProgramData\anaconda3\share\jupyter\nbextensions\keplergl-jupyter\extension.js
Up to date: C:\ProgramData\anaconda3\share\jupyter\nbextensions\keplergl-jupyter\extension.js.map
Up to date: C:\ProgramData\anaconda3\share\jupyter\nbextensions\keplergl-jupyter\index.js
Up to date: C:\ProgramData\anaconda3\share\jupyter\nbextensions\keplergl-jupyter\index.js.map
Up to date: C:\ProgramData\anaconda3\share\jupyter\nbextensions\keplergl-jupyter\keplergl.html
Up to date: C:\ProgramData\anaconda3\share\jupyter\nbextensions\keplergl-jupyter\main.js
- Validating: ok

    To initialize this nbextension in the browser every time the notebook (or other app) loads:
    
          jupyter nbextension enable keplergl --py --sys-prefix
    
Enabling notebook extension keplergl-jupyter/extension...
      - Validating: ok
In [8]:
import numpy as np
from IPython.display import Image
import branca.colormap as cm
import pandas as pd
import seaborn as sns
import plotly.express as px
from plotly.offline import init_notebook_mode, iplot
import seaborn as sns
import matplotlib.pyplot as pl
from sklearn.preprocessing import LabelEncoder
import imblearn
from collections import Counter
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report
from sklearn.metrics import accuracy_score, f1_score,recall_score, precision_score
from sklearn.metrics import average_precision_score, roc_auc_score, roc_curve, auc
from xgboost import XGBClassifier
import matplotlib
import imblearn
import folium
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline
import os , h3
In [9]:
data_path = "C:/Users/asoda/Downloads/Group_22_Asodariya_Rajodiya/updated_Telco_customer_churn.csv"
In [10]:
data = pd.read_csv(data_path)

Data Procesing¶

In [12]:
data
Out[12]:
CustomerID Count Country State City Zip Code Lat Long Latitude Longitude Gender ... Contract Paperless Billing Payment Method Monthly Charges Total Charges Churn Label Churn Value Churn Score CLTV Churn Reason
0 7169-YWAMK 1 United States California Idyllwild 90001 40.587919, -122.464732 38.494162 -121.272414 Male ... Two year Yes Electronic check 47.49 20.2 No 0 47 5429 NaN
1 5940-AHUHD 1 United States California Orosi 95015 37.321233, -120.656354 34.260619 -117.201563 Female ... Two year Yes Electronic check 102.16 996.85 Yes 1 88 5313 Competitor had better devices
2 0916-KNFAJ 1 United States California Tustin 91918 40.936285, -121.572692 41.263143 -120.422128 Female ... Month-to-month Yes Bank transfer (automatic) 70.89 20.2 No 0 23 3388 NaN
3 2034-CGRHZ 1 United States California Martinez 92279 37.890145, -119.184087 41.119480 -122.269998 Male ... Month-to-month Yes Bank transfer (automatic) 95.01 894.3 Yes 1 93 4947 Competitor offered higher download speeds
4 3893-JRNFS 1 United States California Johannesburg 92013 33.313828, -116.940501 34.077048 -116.606626 Male ... One year No Bank transfer (automatic) 18.25 20.2 No 0 66 5541 NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
499995 4373-MAVJG 1 United States California Lynwood 93811 34.057256, -117.667677 37.488996 -117.901978 Female ... Two year Yes Bank transfer (automatic) 40.47 6139.5 No 0 48 3028 NaN
499996 7820-ZYGNY 1 United States California Downieville 92464 41.405193, -123.008567 39.736345 -122.262106 Male ... Two year Yes Mailed check 54.12 20.2 No 0 52 5180 NaN
499997 9885-MFVSU 1 United States California Lancaster 95719 39.84784, -122.544556 38.632932 -119.504116 Male ... Two year Yes Bank transfer (automatic) 62.29 No 0 25 3706 NaN
499998 1842-EZJMK 1 United States California Canoga Park 93816 34.702766, -116.093376 34.367019 -117.627314 Male ... Month-to-month Yes Electronic check 65.36 865.8 Yes 1 100 2003 NaN
499999 6618-RYATB 1 United States California Montrose 90339 35.824572, -116.274755 41.926854 -121.907037 Male ... Month-to-month No Mailed check 18.25 154.85 Yes 0 62 5066 NaN

500000 rows × 33 columns

The "Total Charges" column is currently of object data type, so it needs to be converted to a suitable numerical format.

In [14]:
data['Total Charges'] = pd.to_numeric(data['Total Charges'], errors='coerce')

Examining the dataset for any missing values.

In [16]:
data.isnull().sum()
Out[16]:
CustomerID                0
Count                     0
Country                   0
State                     0
City                      0
Zip Code                  0
Lat Long                  0
Latitude                  0
Longitude                 0
Gender                    0
Senior Citizen            0
Partner                   0
Dependents                0
Tenure Months             0
Phone Service             0
Multiple Lines            0
Internet Service          0
Online Security           0
Online Backup             0
Device Protection         0
Tech Support              0
Streaming TV              0
Streaming Movies          0
Contract                  0
Paperless Billing         0
Payment Method            0
Monthly Charges           0
Total Charges         85696
Churn Label               0
Churn Value               0
Churn Score               0
CLTV                      0
Churn Reason         398021
dtype: int64

The columns "Total Charges" and "Churn Reason" contain missing values. The "Churn Reason" column has a significant number of null entries, as not all customers in the dataset have churned.

In [18]:
data['CustomerID'].nunique()
Out[18]:
7043
In [19]:
data.groupby('Churn Label')['CustomerID'].nunique()
Out[19]:
Churn Label
No     7043
Yes    4903
Name: CustomerID, dtype: int64
In [20]:
data[data['Total Charges'].isna()]
Out[20]:
CustomerID Count Country State City Zip Code Lat Long Latitude Longitude Gender ... Contract Paperless Billing Payment Method Monthly Charges Total Charges Churn Label Churn Value Churn Score CLTV Churn Reason
8 6976-BWGLQ 1 United States California Solana Beach 92032 39.346898, -121.759537 36.655354 -119.593865 Male ... Two year No Bank transfer (automatic) 18.85 NaN No 0 26 4750 NaN
15 4086-WITJG 1 United States California Rough And Ready 91437 39.84784, -122.544556 38.517583 -121.440739 Female ... Two year No Bank transfer (automatic) 20.50 NaN No 0 65 2003 NaN
16 5146-YYFRZ 1 United States California Cobb 92934 41.737962, -123.07557 34.481743 -118.792346 Female ... Two year No Bank transfer (automatic) 46.02 NaN No 0 67 4490 Network reliability
20 4526-RMTLL 1 United States California Happy Camp 94905 40.053684, -120.743116 41.860185 -120.105994 Female ... Month-to-month No Mailed check 18.25 NaN No 0 43 3806 NaN
21 8591-NXRCV 1 United States California Castro Valley 91409 36.596271, -121.442274 37.040184 -120.033492 Male ... Two year Yes Credit card (automatic) 54.34 NaN No 0 80 2161 NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
499971 1982-FEBTD 1 United States California Camino 95842 40.331975, -121.460674 40.165159 -122.381646 Male ... Two year Yes Bank transfer (automatic) 109.35 NaN No 0 73 5931 NaN
499976 2133-TSRRM 1 United States California Rancho Cucamonga 94879 40.053684, -120.743116 33.970335 -117.259627 Male ... Two year No Mailed check 19.19 NaN No 0 57 3129 NaN
499982 4989-LIXVT 1 United States California Pine Grove 90221 38.888351, -123.239647 38.455665 -122.426123 Female ... One year Yes Bank transfer (automatic) 50.81 NaN No 0 70 4046 NaN
499993 4615-PIVVU 1 United States California Downey 93865 40.059385, -122.091481 38.803348 -120.516442 Female ... Two year No Bank transfer (automatic) 18.25 NaN No 0 21 5755 NaN
499997 9885-MFVSU 1 United States California Lancaster 95719 39.84784, -122.544556 38.632932 -119.504116 Male ... Two year Yes Bank transfer (automatic) 62.29 NaN No 0 25 3706 NaN

85696 rows × 33 columns

Based on the analysis, customers with missing values in the "Total Charges" column are under a contract, primarily in Two-Year contracts, with a few in One-Year contracts.

Imputing Missing Values¶

  1. Computing charges
In [24]:
data['calc_charges'] = data['Monthly Charges'] * data['Tenure Months']
  1. Calculating charge discrepancy
In [26]:
data['diff_in_charges'] = data['Total Charges'] - data['calc_charges']

Let’s test our approach!

In [28]:
fig = px.histogram(data, x="diff_in_charges",color = 'Contract',marginal="box")
fig.show()
The graph shows the distribution of the difference between the actual total charges of our customers and the calculated values, which are derived by multiplying the monthly charges by the number of months the customer has used the service.

Quantiles have been plotted to highlight the presence of outliers.

In [31]:
data.groupby('Contract')[['Total Charges','diff_in_charges']].quantile([.50,.80,.90,.95])
Out[31]:
Total Charges diff_in_charges
Contract
Month-to-month 0.50 899.45 -54.320
0.80 3565.65 2091.230
0.90 5308.70 3787.360
0.95 6148.45 5226.713
One year 0.50 333.55 -1150.870
0.80 3078.10 1066.370
0.90 4895.10 3033.025
0.95 6148.45 4601.835
Two year 0.50 44.70 -1364.450
0.80 2381.55 546.830
0.90 4484.05 2598.100
0.95 6033.10 4294.600
In [32]:
data['Total Charges'] = np.where(data['Total Charges'].isna() == True,data['calc_charges'], data['Total Charges'])
In [33]:
data = data.drop(['calc_charges','diff_in_charges'], axis=1)

Exploratory Data Analysis¶

1. Overall Churn Rate¶

To build a customer churn model, it’s essential to first understand the factors that most significantly influence customer churn. Churned customers are those who have stopped using the service.
Let's introduce a key metric — churn rate, which represents the percentage of customers who have churned — and analyze this metric based on the customer characteristics in our dataset.
In [37]:
fig = px.pie(data.groupby('Churn Label')['CustomerID'].nunique().reset_index(),
             values='CustomerID',
             names='Churn Label')
fig.show()

In the dataset, 41% of the customers have churned and stopped using the company's services.

2. Customer Geography¶

Let's begin by examining the geography of the customers. We have data on their city, postal code, and coordinates.

In [41]:
data.groupby(['Country','State'])['CustomerID'].count()
Out[41]:
Country        State     
United States  California    500000
Name: CustomerID, dtype: int64

All of our clients are located in the United States, specifically in California.

In [43]:
fig = px.scatter_mapbox(data.groupby(['Latitude','Longitude'])['CustomerID'].count().reset_index(), lat="Latitude", lon="Longitude", hover_data= ['CustomerID'], zoom=4, height=300)
fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()

The highest concentration of customers is found in the Los Angeles, San Diego, and San Francisco areas.

In [45]:
fig = px.bar(data.groupby(['City'])['CustomerID'].count().reset_index().sort_values('CustomerID',
                                                                                    ascending=False).head(50),
             x='City',
             y='CustomerID',
             color = 'CustomerID',
             text = 'CustomerID')
fig.show()

Add Hexagonal Visualization¶

In [47]:
# Set the resolution level for the hexagons
hex_level = 5

# Assuming `data` is a DataFrame containing 'Latitude', 'Longitude', and 'CustomerID' columns
# Example DataFrame for reference
# data = pd.DataFrame({
#     'Latitude': [37.7749, 34.0522, 40.7128],
#     'Longitude': [-122.4194, -118.2437, -74.0060],
#     'CustomerID': [1, 2, 3]
# })

# Convert geographical coordinates (latitude and longitude) into H3 hex IDs
data['hex_id'] = data.apply(lambda x: h3.latlng_to_cell(x['Latitude'], x['Longitude'], hex_level), axis=1)

# Group the data by hex ID and count the number of customers per hex
hex_counts = data.groupby('hex_id')['CustomerID'].count().reset_index(name='total_clients')

# Calculate the geographic center of each hexagon
hex_counts['center'] = hex_counts['hex_id'].apply(lambda x: h3.cell_to_latlng(x))

# Set the range of values for coloring and create a colormap
color_range = [hex_counts['total_clients'].min(), hex_counts['total_clients'].max()]
colormap = cm.LinearColormap(
    colors=["purple", "red", "orange", "yellow", "green"],
    vmin=min(color_range),
    vmax=max(color_range)
)

# Determine the center of the map based on the average location of hexagon centers
mean_lat = hex_counts['center'].apply(lambda x: x[0]).mean()
mean_lon = hex_counts['center'].apply(lambda x: x[1]).mean()
map_center = [mean_lat, mean_lon]

# Initialize a Folium map with the 'Stamen Terrain' tiles
m = folium.Map(
    location=map_center,
    zoom_start=6,
    tiles='Stamen Terrain',
    attr="Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL."
)

# Add hexagonal regions to the map with color-coding based on customer counts
for _, row in hex_counts.iterrows():
    folium.Polygon(
        locations=h3.cell_to_boundary(row['hex_id']),  # Remove geo_json argument and directly use boundary
        fill=True,
        fill_color=colormap(row['total_clients']),
        fill_opacity=0.7,
        stroke=False,
        tooltip=f"Number of clients: {row['total_clients']}"
    ).add_to(m)

# Add a color legend to the map
colormap.caption = 'Number of clients'
m.add_child(colormap)

# Display the map
m
Out[47]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Calculate churn rate per hexagon

In [49]:
churn = data.assign(churn_clients = np.where(data['Churn Label']=='Yes',data['CustomerID'],None)).groupby(['hex_id']).agg({'churn_clients':'count'}).reset_index()
In [50]:
clients = data.groupby(['hex_id'])['CustomerID'].count().reset_index()
In [51]:
churn_data = clients.join(churn.set_index(['hex_id']), on=['hex_id'])
In [52]:
churn_data['churn_rate'] = churn_data['churn_clients']/churn_data['CustomerID']
In [53]:
churn_data
Out[53]:
hex_id CustomerID churn_clients churn_rate
0 85280043fffffff 9 0 0.000000
1 8528004bfffffff 8 2 0.250000
2 85280207fffffff 24 5 0.208333
3 8528020bfffffff 69 17 0.246377
4 8528020ffffffff 33 6 0.181818
... ... ... ... ...
772 85485b67fffffff 104 24 0.230769
773 85485ba7fffffff 5 0 0.000000
774 85485babfffffff 22 8 0.363636
775 85485baffffffff 21 8 0.380952
776 85485bb7fffffff 8 0 0.000000

777 rows × 4 columns

In [54]:
# Assuming churn_data is a DataFrame containing 'hex_id', 'churn_rate', and 'CustomerID' columns
# Example:
# churn_data = pd.DataFrame({
#     'hex_id': ['8928308280fffff', '8928308283fffff'],
#     'churn_rate': [0.1, 0.4],
#     'CustomerID': [10, 20]
# })

# Compute the geographic center of each hexagon
churn_data['center'] = churn_data['hex_id'].apply(lambda x: h3.cell_to_latlng(x))

# Set the color range based on churn rate and create a colormap
color_range = [churn_data['churn_rate'].min(), churn_data['churn_rate'].max()]
colormap = cm.LinearColormap(
    colors=["green", "orange", "red"],
    vmin=min(color_range),
    vmax=max(color_range)
)

# Determine the central location of the map by averaging the hexagon centers
mean_lat = churn_data['center'].apply(lambda x: x[0]).mean()
mean_lon = churn_data['center'].apply(lambda x: x[1]).mean()
map_center = [mean_lat, mean_lon]

# Create a Folium map centered around the calculated geographic center
m = folium.Map(
    location=map_center,
    zoom_start=6,
    width='100%',
    height='80%',
    tiles='Stamen Terrain',
    attr="Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL."
)

# Add hexagonal regions to the map, color-coded by churn rate
for _, row in churn_data.iterrows():
    folium.Polygon(
        locations=h3.cell_to_boundary(row['hex_id']),  # Use the correct method to get hex boundaries
        fill=True,
        fill_color=colormap(row['churn_rate']),
        fill_opacity=0.7,
        stroke=False,
        tooltip=f"Churn rate: {row['churn_rate']}<br>Number of customers: {row['CustomerID']}"
    ).add_to(m)

# Attach a color legend to the map
colormap.caption = 'Churn rate'
m.add_child(colormap)

# Display the map
m
Out[54]:
Make this Notebook Trusted to load map: File -> Trust Notebook

3) Customer lifetime in service¶

Before analyzing the services customers have used and their other characteristics, it’s crucial to consider how long customers have been using the service and at what point, in terms of months, they typically start to churn.

Let's analyze the number of months that customers who have churned used the service, and determine if there is a specific point at which the majority of customers stop using the service.

In [58]:
fig = px.histogram(data, x="Tenure Months", color="Churn Label",marginal="box" )
fig.show()
In [59]:
data.groupby('Churn Label')['Tenure Months'].quantile([.50,.75,.90,.95])
Out[59]:
Churn Label      
No           0.50    50.0
             0.75    64.0
             0.90    71.0
             0.95    72.0
Yes          0.50    10.0
             0.75    42.0
             0.90    63.0
             0.95    68.0
Name: Tenure Months, dtype: float64
In [60]:
data.groupby('Churn Label')['Tenure Months'].mean()
Out[60]:
Churn Label
No     42.461902
Yes    22.512375
Name: Tenure Months, dtype: float64

Approximately 50% of the customers who churned did so within the first 10 months. The rate of churn declines sharply after 5 months of service.

The client's lifetime before churn is crucial information. Typically, the first few months of service are the riskiest, as customers may have high expectations, which, if unmet, can lead to churn.

What are the factors that lead to customer churn?¶

A total of 41% of customers have stopped using the service, with 50% of them having used the service for less than 10 months.

Now, we can begin analyzing the customer account data to identify which types of customers are more likely to churn and determine the actions we can take to address this.

Before diving into the analysis, we can also examine the responses in the "Churn Reason" column. While customer survey data can be biased due to its subjective nature, gathering customer feedback is essential for any business aiming to grow and improve. Additionally, we have plenty of other data to cross-check and validate these customer responses.

In [67]:
fig = px.bar(data.groupby(['Churn Reason'])['CustomerID'].count().reset_index().sort_values('CustomerID',
                                                                                    ascending=False),
             x='Churn Reason',
             y='CustomerID',
             color = 'CustomerID',
             text = 'CustomerID')
fig.show()

Among the 41% of customers who churned, the majority left due to issues related to data, speed, and devices, which were better offered by competitors. The remaining churned customers cited poor customer service or dissatisfaction with the attitude of support specialists or the provider as their reasons.

There are also several reasons for churn that are beyond our control, such as customer relocation. Since this data is sparse and irrelevant to understanding how we can retain customers, I will remove it from the dataset.

In [70]:
data = data[data['Churn Reason'] != 'Moved']
data = data[data['Churn Reason'] != 'Deceased']

Types of Contracts¶

Let’s examine the different types of contracts offered by the service and analyze how they impact the churn rate.

In [73]:
fig = px.histogram(data, x="Churn Label", color="Contract", barmode="group",
                   title="Number of customers by contract type")
fig.update_layout(width=700, height=500, bargap=0.1)
fig.show()
In [74]:
fig = px.pie(data.groupby(['Contract','Churn Label'])['CustomerID'].count().reset_index(),
             values='CustomerID',
            names='Contract',
            facet_col = 'Churn Label',
            title = 'Churn rate by contract type')

fig.show()

86% of the customers who churned had a month-to-month contract.

In [76]:
data.groupby(['Contract','Churn Label'])['Tenure Months'].mean()
Out[76]:
Contract        Churn Label
Month-to-month  No             37.127550
                Yes            20.874050
One year        No             46.603460
                Yes            31.075628
Two year        No             49.397881
                Yes            35.643694
Name: Tenure Months, dtype: float64

Total and monthly charges for clients¶

When examining the total and monthly charges of customers, the monthly charges may be more relevant for identifying the causes of churn. However, total charges can also provide valuable insights in understanding customer behavior.

Total charges

In [80]:
fig = px.histogram(data, x="Total Charges", color="Churn Label",
                   marginal="box"
                  )
fig.show()
The median charges of customers who have churned are more than twice as low as those of customers who continue using the service.
However, this doesn't necessarily imply that churned customers were less financially capable. As we’ve seen, many customers tend to leave the service within the first 5 months, which could explain the lower charges.

Therefore, we should focus on the monthly charges of customers to gain better insights.

Monthly Charges

In [84]:
fig = px.histogram(data, x="Monthly Charges", color="Churn Label",
                   marginal="box"
                  )
fig.show()
In [85]:
data.groupby('Churn Label')['Monthly Charges'].quantile([.50,.75,.95,.99])
Out[85]:
Churn Label      
No           0.50     51.54
             0.75     84.22
             0.95    107.15
             0.99    114.26
Yes          0.50     76.90
             0.75     96.91
             0.95    111.11
             0.99    116.80
Name: Monthly Charges, dtype: float64

The median monthly charges of customers who have churned are higher than those of active customers. Since we did not identify a strong correlation between churn and customer location in previous steps, this difference may be related to specific services or other factors. We will investigate further to uncover the reasons!

Services utilized by the Customer¶

Here is a list of services utilized by each customer:

  • Phone Service
  • Internet Service
  • Online Security
  • Online Backup
  • Device Protection
  • Multiple Lines
  • Tech Support
  • Streaming TV
  • Streaming Movies

Given the large list of services, let’s prioritize by analyzing which variables correlate most strongly with the churn variable. This will help us focus on the most impactful factors.

In [90]:
corr_df = data.copy()
In [264]:
corr_df['Churn Label'] = corr_df['Churn Label'].replace({'Yes': 1, 'No': 0}).astype(int)
In [92]:
df_dummies = pd.get_dummies(corr_df[['Churn Label','Phone Service','Multiple Lines','Internet Service','Online Security',
                                 'Online Backup','Device Protection','Tech Support','Streaming TV',
                                 'Streaming Movies']])
df_dummies.head()
Out[92]:
Churn Label Phone Service_No Phone Service_Yes Multiple Lines_No Multiple Lines_No phone service Multiple Lines_Yes Internet Service_DSL Internet Service_Fiber optic Internet Service_No Online Security_No ... Device Protection_Yes Tech Support_No Tech Support_No internet service Tech Support_Yes Streaming TV_No Streaming TV_No internet service Streaming TV_Yes Streaming Movies_No Streaming Movies_No internet service Streaming Movies_Yes
0 0 False True False False True True False False True ... True False False True False False True True False False
1 1 False True False False True False True False False ... False True False False False False True False False True
2 0 False True False True False True False False False ... False True False False True False False True False False
3 1 False True False True False False True False True ... False True False False True False False False False True
4 0 False True False False True False False True False ... False False True False False True False False True False

5 rows × 27 columns

In [93]:
pl.figure(figsize=(9, 7))
sns.heatmap(df_dummies.corr(), annot=False, cmap='coolwarm')

pl.show()
No description has been provided for this image

Internet services¶

First, let's examine the types of internet services that customers have, and then analyze how these different types affect the churn rate.

In [96]:
fig = px.bar(data.groupby('Internet Service')['CustomerID'].count().reset_index(),
             x='Internet Service',
             y='CustomerID',
             color = 'Internet Service',
             text = 'CustomerID')
fig.show()

The majority of customers are connected to DSL Internet, with a group of customers who do not use internet services (likely using only phone services). Interestingly, the churn rate among these non-internet users appears to be lower, according to the correlation graph, but we will verify this further.

Let's analyze which types of internet services were used by customers who churned. This will help identify if certain types of internet services are more associated with churn.

In [99]:
fig = px.pie(data.groupby(['Internet Service','Churn Label'])['CustomerID'].count().reset_index(),
             values='CustomerID',
             facet_col = 'Churn Label',
             names='Internet Service',
            title = 'What type of internet was associated with the customers who left the service?')
fig.show()

Approximately 45% of customers who churned were using Fiber Optic Internet services before leaving.

Tech support and online security¶

Tech Support

In [103]:
fig = px.bar(data.groupby(['Internet Service',
                                                'Tech Support',
                                                'Churn Label'])['CustomerID'].count().reset_index(),
             x="Internet Service",
             y="CustomerID",
             color="Churn Label",
             text = 'CustomerID',
             barmode="group",
             facet_col="Tech Support"
            )
fig.show()

After consulting with Tech Support, the churn rate is lower. We observe that even among customers with Fiber Optic Internet, the churn percentage is lower for those who have the Tech Support service enabled.

In [105]:
fig = px.pie(data.groupby(['Tech Support','Churn Label'])['CustomerID'].count().reset_index(),
             values='CustomerID',
             facet_col = 'Churn Label',
             hole = .5,
             names='Tech Support',
            title = 'Tech support option and churn')
fig.show()

70.1% of the customers who churned did not have the Tech Support option enabled.

Customer's Payment Method¶

Let’s analyze the payment methods used by customers and examine how these methods influence the churn rate.

In [109]:
fig = px.bar(data.groupby(['Payment Method',
                                                'Churn Label'])['CustomerID'].count().reset_index(),
             x="CustomerID",
             y="Payment Method",
             color="Churn Label",
             text = 'CustomerID'
            )
fig.show()

Wow, it appears that for customers who use an electronic check as their payment method, the churn rate is around 50%.

In [111]:
fig = px.pie(data.groupby(['Payment Method','Churn Label'])['CustomerID'].count().reset_index(),
            values='CustomerID',
            names='Churn Label',
            facet_col = 'Payment Method',
            color = 'Churn Label',
            title = 'Churn rate by customer payment method')

fig.show()

We can observe that customers who use automatic payment methods, such as credit cards and bank transfers, generally have a lower churn rate compared to those using electronic or mailed checks.

Among the customers with Fiber Optic Internet, the majority used an electronic check as their payment method.

In [114]:
churn_pm = data.assign(churn_clients = np.where(data['Churn Label']== 'Yes',data['CustomerID'],None))\
   .groupby(['Payment Method','Internet Service']).agg({'churn_clients':'count'}).reset_index()
In [115]:
pm_clients = data.groupby(['Payment Method','Internet Service'])['CustomerID'].count().reset_index()
In [116]:
pm_data = pm_clients.join(churn_pm.set_index(['Payment Method','Internet Service']), on=['Payment Method','Internet Service'])
In [117]:
pm_data
Out[117]:
Payment Method Internet Service CustomerID churn_clients
0 Bank transfer (automatic) DSL 89543 15276
1 Bank transfer (automatic) Fiber optic 47154 16947
2 Bank transfer (automatic) No 39499 6590
3 Credit card (automatic) DSL 30307 5479
4 Credit card (automatic) Fiber optic 15042 5676
5 Credit card (automatic) No 8498 1381
6 Electronic check DSL 78026 35475
7 Electronic check Fiber optic 62441 43093
8 Electronic check No 5139 1818
9 Mailed check DSL 57424 15361
10 Mailed check Fiber optic 18577 9420
11 Mailed check No 45334 10811
In [118]:
pm_data['churn_rate,%'] = round(((pm_data['churn_clients']/pm_data['CustomerID']) * 100),2)
In [119]:
fig = px.bar(pm_data.sort_values('churn_rate,%'),
             x='churn_rate,%',
             y='Payment Method',
             facet_col = 'Internet Service',
             color = 'churn_rate,%',
             text = 'churn_rate,%')
fig.show()

Gender and Age of Clients¶

Customer's Gender

In [122]:
fig = px.pie(data.groupby('Gender')['CustomerID'].count().reset_index(),
            values='CustomerID',
            names='Gender',
            color_discrete_sequence=px.colors.sequential.RdBu,
            title = 'Distribution of the clients by gender')

fig.show()

Let's analyze the churn rate by gender to identify any differences between men and women.

In [124]:
fig = px.bar(data.groupby(['Gender',
                                                'Churn Label'])['CustomerID'].count().reset_index(),
             x="CustomerID",
             y="Gender",
             color="Churn Label",
             text = 'CustomerID'
            )
fig.show()

Senior Citizen or not

In [126]:
fig = px.pie(data.groupby(['Senior Citizen','Churn Label'])['CustomerID'].count().reset_index(),
            values='CustomerID',
            names='Churn Label',
            facet_col = 'Senior Citizen',
            color = 'Churn Label',
            title = 'Churn rate by customer age')

fig.show()

The churn rate among senior citizens is nearly twice as high as that of non-senior citizens. However, the number of senior citizens in the dataset is much smaller.

In [128]:
data.groupby('Senior Citizen')['CustomerID'].count()
Out[128]:
Senior Citizen
No     453376
Yes     43608
Name: CustomerID, dtype: int64

Effect of having a partner or dependents on churn rate.¶

In [130]:
fig = px.bar(data.groupby(['Senior Citizen','Partner',
                                        'Dependents','Churn Label'])['CustomerID'].count().reset_index(),
             x="Senior Citizen",
             y="CustomerID",
             color="Churn Label",
             #barmode="group",
             facet_row="Partner",
             facet_col = 'Dependents'
            )
fig.show()

We observe that among senior citizens without a partner or dependents, the churn rate is nearly 50%.

Let's examine which services were used by senior citizens, and then proceed to summarize the findings and begin building a churn prediction model.

In [133]:
fig = px.bar(data.groupby(['Senior Citizen','Internet Service','Churn Label'])['CustomerID'].count().reset_index(),
             x="Internet Service",
             y="CustomerID",
             color="Churn Label",
             barmode="group",
             facet_col = 'Senior Citizen'
            )
fig.show()

Among senior citizens, a larger percentage were connected to Fiber Optic Internet, and these customers exhibit the highest churn rate.

Here’s a summary of what we learned from analyzing the data:

  • The lowest churn rate is observed among customers without Internet services (though this group is smaller).
  • 69.2% of churned customers were using Fiber Optic Internet.
  • The lack of technical support and online security options is correlated with higher churn.
  • When considering payment methods, customers using electronic checks have the highest churn rate, regardless of the type of Internet service.
  • The churn rate for senior citizens is nearly twice as high as that for non-senior citizens.

Feature Engineering¶

1) Removing the columns that are not needed.¶

In [138]:
data = data.drop(['Country','State','Count','Zip Code','Churn Reason','City','Churn Score','Churn Value','CLTV','CustomerID','Lat Long',
                  'Latitude','Longitude'], axis = 1)
In [139]:
data.info()
<class 'pandas.core.frame.DataFrame'>
Index: 496984 entries, 0 to 499999
Data columns (total 21 columns):
 #   Column             Non-Null Count   Dtype  
---  ------             --------------   -----  
 0   Gender             496984 non-null  object 
 1   Senior Citizen     496984 non-null  object 
 2   Partner            496984 non-null  object 
 3   Dependents         496984 non-null  object 
 4   Tenure Months      496984 non-null  int64  
 5   Phone Service      496984 non-null  object 
 6   Multiple Lines     496984 non-null  object 
 7   Internet Service   496984 non-null  object 
 8   Online Security    496984 non-null  object 
 9   Online Backup      496984 non-null  object 
 10  Device Protection  496984 non-null  object 
 11  Tech Support       496984 non-null  object 
 12  Streaming TV       496984 non-null  object 
 13  Streaming Movies   496984 non-null  object 
 14  Contract           496984 non-null  object 
 15  Paperless Billing  496984 non-null  object 
 16  Payment Method     496984 non-null  object 
 17  Monthly Charges    496984 non-null  float64
 18  Total Charges      496984 non-null  float64
 19  Churn Label        496984 non-null  object 
 20  hex_id             496984 non-null  object 
dtypes: float64(2), int64(1), object(18)
memory usage: 83.4+ MB

2) Converting categorical variables to numeric values.¶

In [141]:
corr_df['Churn Label'] = corr_df['Churn Label'].replace(to_replace='Yes', value=1)
corr_df['Churn Label'] = corr_df['Churn Label'].replace(to_replace='No', value=0)
In [142]:
def encode_data(dataframe_series):
    if dataframe_series.dtype=='object':
        dataframe_series = LabelEncoder().fit_transform(dataframe_series)
    return dataframe_series
In [143]:
data = data.apply(lambda x: encode_data(x))
data.head()
Out[143]:
Gender Senior Citizen Partner Dependents Tenure Months Phone Service Multiple Lines Internet Service Online Security Online Backup ... Tech Support Streaming TV Streaming Movies Contract Paperless Billing Payment Method Monthly Charges Total Charges Churn Label hex_id
0 1 0 1 1 72 1 2 0 0 0 ... 2 2 0 2 1 2 47.49 20.20 0 240
1 0 0 0 1 3 1 2 1 2 0 ... 0 2 2 2 1 2 102.16 996.85 1 470
2 0 0 1 0 55 1 1 0 2 2 ... 0 0 0 0 1 0 70.89 20.20 0 83
3 1 0 0 1 3 1 1 1 0 2 ... 0 0 2 0 1 0 95.01 894.30 1 70
4 1 0 0 0 16 1 2 2 1 1 ... 1 1 1 1 0 0 18.25 20.20 0 460

5 rows × 21 columns

Now, let's examine the correlation between all the selected features and the churn label.

In [145]:
fig = px.bar(data.corr()['Churn Label'].sort_values(ascending = False),
             color = 'value')
fig.show()

3) Balancing the Dataset¶

We observe that the data is unbalanced, with a higher number of customers who have not churned compared to those who have.

In [148]:
data.groupby('Churn Label')['Churn Label'].count()
Out[148]:
Churn Label
0    329657
1    167327
Name: Churn Label, dtype: int64
In [149]:
over = SMOTE(sampling_strategy = 1)
x = data.drop("Churn Label", axis = 1).values
y = data['Churn Label'].values
In [150]:
x,y = over.fit_resample(x,y)

Modeling using machine learning approaches.¶

We are comparing three algorithms here:

  1. Logistic Regression
  2. Random Forest
  3. Extreme Gradient Boosting Classifier

Why these three algorithms for Telecommunications?¶

  • Logistic Regression:

    • Simple and interpretable model, easy to understand and implement.
    • Effective for binary classification tasks like churn prediction.
    • Outputs probabilities of churn, which is useful for assessing risk.
  • Random Forest:

    • An ensemble method that merges multiple decision trees for improved performance.
    • Less prone to overfitting, capable of handling complex, non-linear relationships.
    • Provides feature importance scores to identify key factors influencing churn.
  • Extreme Gradient Boosting Classifier (XGBoost):

    • A gradient boosting method that combines multiple weak learners into a strong one.
    • Known for its high accuracy and efficiency, often outperforming other models in churn prediction.
    • Handles both linear and non-linear relationships, making it ideal for more complex churn datasets.

Divide the data into training and test sets for the algorithms.¶

In [155]:
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state =2, test_size = 0.2)
In [156]:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
from imblearn.over_sampling import SMOTE
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score,recall_score, precision_score
from sklearn.metrics import average_precision_score, roc_auc_score, roc_curve, auc
from xgboost import XGBClassifier
import matplotlib
import folium
import os , h3
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
In [157]:
def logistic_regression(x, y):
    if len(x) < 10 or len(y) < 10:
        print("Insufficient data.")
        return

    try:
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
        #'liblinear' for binary classification
        model = LogisticRegression(solver='liblinear')
        model.fit(x_train, y_train)
        y_pred = model.predict(x_test)
        metrics = {
            "Accuracy": accuracy_score(y_test, y_pred),
            "F1 Score": f1_score(y_test, y_pred),
            "Recall": recall_score(y_test, y_pred),
            "Precision": precision_score(y_test, y_pred),
            "AUC": roc_auc_score(y_test, model.predict_proba(x_test)[:, 1])
        }

        for metric, value in metrics.items():
            print(f"{metric}: {value}")
    except Exception as e:
        print(f"An error occurred: {e}")
In [158]:
from sklearn.model_selection import RandomizedSearchCV
from sklearn.linear_model import LogisticRegression

def tune_logistic_regression(x, y):
    if len(x) < 10 or len(y) < 10:
        print("Insufficient data.")
        return

    param_distributions = {
        'C': [0.1, 1, 10],
        'penalty': ['l1', 'l2'],
        'solver': ['liblinear', 'saga'],
        'max_iter': [1000]
    }

    try:
        random_search = RandomizedSearchCV(LogisticRegression(), param_distributions, n_iter=10, cv=5, scoring='roc_auc', n_jobs=-1)
        random_search.fit(x, y)
        best_params = random_search.best_params_
        print(f"Best parameters: {best_params}")
        logistic_regression(x, y)
    except Exception as e:
        print(f"An error occurred during tuning: {e}")
In [159]:
def random_forest(x, y):
    if len(x) < 10 or len(y) < 10:
        print("Insufficient data.")
        return

    try:
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
        model = RandomForestClassifier()
        model.fit(x_train, y_train)
        y_pred = model.predict(x_test)
        metrics = {
            "Accuracy": accuracy_score(y_test, y_pred),
            "F1 Score": f1_score(y_test, y_pred),
            "Recall": recall_score(y_test, y_pred),
            "Precision": precision_score(y_test, y_pred),
            "AUC": roc_auc_score(y_test, model.predict_proba(x_test)[:, 1])
        }

        for metric, value in metrics.items():
            print(f"{metric}: {value}")
    except Exception as e:
        print(f"An error occurred: {e}")
In [160]:
from sklearn.model_selection import RandomizedSearchCV
from sklearn.ensemble import RandomForestClassifier

def tune_random_forest(x, y):
    if len(x) < 10 or len(y) < 10:
        print("Insufficient data.")
        return

    param_distributions = {
        'n_estimators': [50, 100, 200],
        'max_depth': [None, 10, 20],
        'min_samples_split': [2, 5, 10],
        'min_samples_leaf': [1, 2, 4]
    }

    try:
        random_search = RandomizedSearchCV(RandomForestClassifier(), param_distributions, n_iter=10, cv=5, scoring='roc_auc', n_jobs=-1)
        random_search.fit(x, y)
        best_params = random_search.best_params_
        print(f"Best parameters: {best_params}")
        random_forest(x, y)  # Evaluate model with best parameters
    except Exception as e:
        print(f"An error occurred during tuning: {e}")
In [161]:
def xgboost(x, y, params=None):
    # Check for insufficient data
    if len(x) < 10 or len(y) < 10:
        print("Insufficient data.")
        return

    try:
        # Split the data into training and testing sets
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

        # Use the provided parameters or default ones
        if params is None:
            params = {
                'eval_metric': 'logloss',
                'objective': 'binary:logistic',
                'random_state': 42
            }

        # Create the XGBoost classifier with the given parameters
        model = XGBClassifier(**params)

        # Fit the model on the training data
        model.fit(x_train, y_train)

        # Make predictions on the test data
        y_pred = model.predict(x_test)
        y_pred_proba = model.predict_proba(x_test)[:, 1]

        # Compute evaluation metrics
        metrics = {
            "Accuracy": accuracy_score(y_test, y_pred),
            "F1 Score": f1_score(y_test, y_pred),
            "Recall": recall_score(y_test, y_pred),
            "Precision": precision_score(y_test, y_pred),
            "AUC": roc_auc_score(y_test, y_pred_proba)
        }

        # Print the evaluation metrics
        for metric, value in metrics.items():
            print(f"{metric}: {value}")

    except Exception as e:
        print(f"An error occurred: {e}")
In [162]:
def tune_xgboost(x, y):
    if len(x) < 10 or len(y) < 10:
        print("Insufficient data.")
        return

    # Parameter grid for RandomizedSearchCV
    param_distributions = {
        'n_estimators': [50, 100, 200],
        'max_depth': [5, 10, 15],
        'learning_rate': [0.01, 0.1, 0.3],
        'subsample': [0.5, 0.7, 0.9],
        'colsample_bytree': [0.5, 0.7, 0.9]
    }

    try:
        # Randomized search for hyperparameter tuning
        random_search = RandomizedSearchCV(
            XGBClassifier(eval_metric='logloss', objective='binary:logistic', random_state=42),
            param_distributions,
            n_iter=10,
            cv=5,
            scoring='roc_auc',
            n_jobs=-1,
            random_state=42
        )
        random_search.fit(x, y)

        # Best parameters
        best_params = random_search.best_params_
        print(f"Best parameters: {best_params}")

        # Evaluate the model with the best parameters
        xgboost(x, y, params={**best_params, 'eval_metric': 'logloss', 'objective': 'binary:logistic', 'random_state': 42})

    except Exception as e:
        print(f"An error occurred during tuning: {e}")
In [163]:
logistic_regression(x, y)
Accuracy: 0.7732950107308343
F1 Score: 0.7779642888974717
Recall: 0.794679979363297
Precision: 0.7619373235953094
AUC: 0.8546811925508556
In [164]:
random_forest(x, y)
Accuracy: 0.8446948727087963
F1 Score: 0.8403433409475399
Recall: 0.8178052259415496
Precision: 0.864158930203473
AUC: 0.930093103886478
In [165]:
xgboost(x, y)
Accuracy: 0.8513229639853485
F1 Score: 0.8478144430730538
Recall: 0.8286394950077388
Precision: 0.8678978401487579
AUC: 0.9368947730086598
In [166]:
tune_logistic_regression(x, y)
Best parameters: {'solver': 'liblinear', 'penalty': 'l1', 'max_iter': 1000, 'C': 0.1}
Accuracy: 0.7732950107308343
F1 Score: 0.7779642888974717
Recall: 0.794679979363297
Precision: 0.7619373235953094
AUC: 0.8546811925508556
In [167]:
# Random forest Hyper Parameter Tuning
tune_random_forest(x, y)
Best parameters: {'n_estimators': 100, 'min_samples_split': 10, 'min_samples_leaf': 2, 'max_depth': None}
Accuracy: 0.8443763603133555
F1 Score: 0.8400109149027404
Recall: 0.8174562228763922
Precision: 0.8638455494443821
AUC: 0.9299547137821632
In [168]:
# XGboost hyperparameter tuning
tune_xgboost(x, y)
Best parameters: {'subsample': 0.5, 'n_estimators': 200, 'max_depth': 15, 'learning_rate': 0.01, 'colsample_bytree': 0.5}
Accuracy: 0.8503674267990262
F1 Score: 0.8451195101848581
Recall: 0.8168492610239446
Precision: 0.8754167140975395
AUC: 0.9353811000643291
In [169]:
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier

# Function to plot ROC curves for different models
def plot_roc_curves(models, x_test, y_test):
    plt.figure(figsize=(12, 8))  # Set figure size for the plot
    for model_name, model in models.items():
        # Get predicted probabilities for the positive class
        y_pred_proba = model.predict_proba(x_test)[:, 1]
        # Compute the false positive rate (FPR) and true positive rate (TPR)
        fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
        # Calculate the AUC (Area Under the Curve)
        roc_auc = auc(fpr, tpr)
        # Plot ROC curve for the current model
        plt.plot(fpr, tpr, label=f"{model_name} (AUC = {roc_auc:.2f})")
    plt.plot([0, 1], [0, 1], 'r--')  # Plot diagonal line representing random chance
    plt.xlabel('False Positive Rate')  # Label for X-axis
    plt.ylabel('True Positive Rate')  # Label for Y-axis
    plt.title('ROC Curves for Churn Prediction Models')  # Title for the plot
    plt.legend()  # Show legend with model names and AUC values
    plt.show()  # Display the plot

# Initialize models for Logistic Regression, Random Forest, and XGBoost
models = {
    "Logistic Regression": LogisticRegression(solver='liblinear'),
    "Random Forest": RandomForestClassifier(),
    "XGBoost": XGBClassifier(eval_metric='logloss')  # Removed use_label_encoder
}

# Train each model using the training data
for model_name, model in models.items():
    model.fit(x_train, y_train)

# Generate and display ROC curves for all models
plot_roc_curves(models, x_test, y_test)
No description has been provided for this image
In [170]:
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score, f1_score, recall_score, precision_score, roc_auc_score

def model_prediction_visualization(x, y):
    # Split the dataset into training and testing sets
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

    # Initialize models for comparison
    models = {
        'Logistic Regression': LogisticRegression(solver='liblinear'),
        'Random Forest': RandomForestClassifier(),
        'XGBoost': XGBClassifier(eval_metric='logloss')  # Removed use_label_encoder
    }

    # Train the models on the training data
    for model_name, model in models.items():
        model.fit(x_train, y_train)

    # Make predictions and compute probabilities with each model on the test data
    predictions = {}
    probabilities = {}
    for model_name, model in models.items():
        predictions[model_name] = model.predict(x_test)  # Predicted labels
        if hasattr(model, "predict_proba"):  # Check if the model supports predict_proba
            probabilities[model_name] = model.predict_proba(x_test)[:, 1]  # Probabilities for positive class

    # Define evaluation metrics to assess model performance
    metrics = {
        'Accuracy': lambda y_true, y_pred: accuracy_score(y_true, y_pred),
        'F1 Score': lambda y_true, y_pred: f1_score(y_true, y_pred),
        'Recall': lambda y_true, y_pred: recall_score(y_true, y_pred),
        'Precision': lambda y_true, y_pred: precision_score(y_true, y_pred),
        'AUC': lambda y_true, y_proba: roc_auc_score(y_true, y_proba)
    }

    # Evaluate each model using the defined metrics
    results = {}
    for model_name in models.keys():
        results[model_name] = {}
        for metric_name, metric_function in metrics.items():
            if metric_name == 'AUC' and model_name in probabilities:
                results[model_name][metric_name] = metric_function(y_test, probabilities[model_name])
            elif metric_name != 'AUC':
                results[model_name][metric_name] = metric_function(y_test, predictions[model_name])

    # Visualize the results by plotting the metric scores for each model
    fig, ax = plt.subplots(figsize=(12, 8))
    plt.title('Comparison of Model Performance')
    plt.xlabel('Metric')
    plt.ylabel('Score')

    # Plot the scores for each model
    for model_name, metric_scores in results.items():
        plt.plot(list(metric_scores.keys()), list(metric_scores.values()), label=model_name, marker='o')

    plt.legend()
    plt.grid()
    plt.show()

# Call the function to visualize model predictions
model_prediction_visualization(x, y)
No description has been provided for this image
In [171]:
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix
from xgboost import XGBClassifier

# Function to display confusion matrices for multiple models
def plot_confusion_matrix(models, x_train, y_train, x_test, y_test):
    # Create a subplot for each model to visualize the confusion matrix
    fig, axs = plt.subplots(1, len(models), figsize=(20, 5))

    # For each model, generate and plot its confusion matrix
    for i, (model_name, model) in enumerate(models.items()):
        # Train the model
        model.fit(x_train, y_train)
        # Predict the labels on the test set
        y_pred = model.predict(x_test)
        # Compute the confusion matrix
        cm = confusion_matrix(y_test, y_pred)
        # Visualize the confusion matrix using a heatmap
        sns.heatmap(cm, annot=True, fmt='d', ax=axs[i], cmap='Blues')
        axs[i].set_title(f'{model_name} Confusion Matrix')  # Set the title for the subplot
        axs[i].set_xlabel('Predicted Label')  # Label for X-axis
        axs[i].set_ylabel('True Label')  # Label for Y-axis

    # Adjust layout for better presentation
    plt.tight_layout()
    plt.show()  # Display the confusion matrix plots

# Define the models to evaluate
models = {
    'Logistic Regression': LogisticRegression(solver='liblinear'),
    'Random Forest': RandomForestClassifier(),
    'XGBoost': XGBClassifier(eval_metric='logloss')  # Removed use_label_encoder
}

# Call the function to plot confusion matrices for the models
plot_confusion_matrix(models, x_train, y_train, x_test, y_test)
No description has been provided for this image

Error Analysis¶

Error analysis is the process of identifying and understanding the sources of errors in a model or system. It involves examining the model’s predictions and comparing them to the actual outcomes to identify discrepancies.

Here’s a step-by-step approach to error analysis:

  1. Collect Data: Gather data on the model’s predictions and actual outcomes, including input features, predicted values, and true values.

  2. Analyze Errors: Calculate error metrics such as mean absolute error, root mean squared error, or classification accuracy to assess the model’s performance.

  3. Identify Patterns: Look for patterns in the errors. For example, are there specific features or conditions causing higher error rates?

  4. Investigate Causes: Examine the underlying causes of the errors, such as model assumptions, data quality issues, or limitations in the model’s design or training process.

  5. Develop Mitigation Strategies: Propose solutions to address the identified errors, such as improving data quality, adjusting model parameters, or testing alternative modeling techniques.

In [173]:
from sklearn.metrics import (
    accuracy_score,
    f1_score,
    recall_score,
    precision_score,
)
import matplotlib.pyplot as plt

# Function to perform error analysis on multiple classification models
def error_analysis(models, x_train, y_train, x_test, y_test):
    # Generate predictions for each model
    predictions = {}
    for model_name, model in models.items():
        model.fit(x_train, y_train)  # Train each model using the training data
        predictions[model_name] = model.predict(x_test)  # Get predictions for the test set

    # Define the evaluation metrics to be calculated
    metrics = {
        "Accuracy": accuracy_score,  # Accuracy of the model
        "F1 Score": f1_score,  # F1 Score
        "Recall": recall_score,  # Recall value
        "Precision": precision_score,  # Precision score
    }

    # Store the results of the error metrics
    results = {}
    for model_name, prediction in predictions.items():
        results[model_name] = {}
        for metric_name, metric_function in metrics.items():
            results[model_name][metric_name] = metric_function(y_test, prediction)

    # Print out the error analysis results
    print("Error Analysis Results:")
    for model_name, metric_scores in results.items():
        print(f"{model_name}:")
        for metric_name, metric_score in metric_scores.items():
            print(f"\t{metric_name}: {metric_score:.4f}")

    # Bar plot for comparison of models' metrics
    metric_names = list(metrics.keys())
    fig, axs = plt.subplots(1, len(metric_names), figsize=(20, 5), sharey=True)
    for i, metric_name in enumerate(metric_names):
        metric_values = [results[model_name][metric_name] for model_name in models.keys()]
        axs[i].bar(models.keys(), metric_values, color="skyblue")
        axs[i].set_title(f"{metric_name} Comparison")
        axs[i].set_xticks(range(len(models)))  # Explicitly set tick positions
        axs[i].set_xticklabels(models.keys(), rotation=45)  # Set tick labels
        axs[i].set_ylim([0, 1])  # Metrics are between 0 and 1
        axs[i].set_ylabel(metric_name)

    plt.tight_layout()
    plt.show()

# Define models for evaluation
models = {
    "Logistic Regression": LogisticRegression(solver="liblinear"),
    "Random Forest": RandomForestClassifier(),
    "XGBoost": XGBClassifier(eval_metric="logloss"),  # Removed use_label_encoder
}

# Perform error analysis on the models
error_analysis(models, x_train, y_train, x_test, y_test)
Error Analysis Results:
Logistic Regression:
	Accuracy: 0.7751
	F1 Score: 0.7802
	Recall: 0.7957
	Precision: 0.7653
Random Forest:
	Accuracy: 0.8437
	F1 Score: 0.8394
	Recall: 0.8145
	Precision: 0.8659
XGBoost:
	Accuracy: 0.8514
	F1 Score: 0.8482
	Recall: 0.8273
	Precision: 0.8701
No description has been provided for this image

Conclusion¶

In this project, we explored Telecommunication Churn Prediction, starting with dataset analysis, followed by feature engineering, and concluding with comparing three machine learning models to identify the best fit for the dataset in terms of accuracy.

The models used were Logistic Regression, Random Forest, and XG-Boost. Among the three, XG-Boost outperformed the others, achieving an accuracy of 85.03%, an F1 score of 84.51%, a precision of 87.54%, and a recall of 81.68%. The ROC curve demonstrated that XG-Boost had the highest AUC value of 0.93, indicating a strong ability to differentiate between churn and non-churn customers.

Error analysis revealed that all models exhibited similar error patterns, suggesting that further tuning or additional features could improve the accuracy, especially for certain customer segments.

In conclusion, the XG-Boost model demonstrated superior performance for churn prediction in the telecommunications industry. With further optimization, it can provide valuable insights for customer retention strategies.